package com.k2data.k2app.util.cas;


import com.k2data.k2app.common.K2Assert;
import com.k2data.k2app.common.MyConstant;
import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.net.URLCodec;
import org.apache.http.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;

public class CasHttpClient {

    private String targetUrl;
    private String casLoginUrl;
    private String token;
    private String tokenParamName = "token";
    private String serviceParamName = "service";
    private String urlEncoding = "utf-8";

    private TrustStrategy trustStrategy;
    private HostnameVerifier hostnameVerifier;

    private HttpClient httpClient;
    private CookieStore cookieStore = new BasicCookieStore();


    public CasHttpClient(String casLoginUrl, String token, String preLogUrl) throws Exception {
        this.casLoginUrl = casLoginUrl;
        this.token = token;
        this.targetUrl = preLogUrl;
        initSslHttpClient();
    }

    public CasHttpClient(String casLoginUrl) throws Exception {
        this.casLoginUrl = casLoginUrl;
        initSslHttpClient();
    }

    public HttpResponse doGet(HttpGet httpGet) throws Exception {
        HttpResponse httpResponse = null;
        try {
            this.preLogin();
            httpResponse = httpClient.execute(httpGet);
        } catch (IOException e) {
            K2Assert.connectionException("casHttpClient doGet failed ",e);
        }
        return httpResponse;
    }

    public HttpResponse doPost(HttpPost httpPost) throws Exception {
        HttpResponse httpResponse = null;
        try {
            this.preLogin();
            httpResponse = httpClient.execute(httpPost);
        } catch (IOException e) {
            K2Assert.connectionException("casHttpClient doPost failed ",e);

        }
        return httpResponse;
    }

    public HttpResponse doPut(HttpPut httpPut) throws Exception {
        HttpResponse httpResponse = null;
        try {
            this.preLogin();
            httpResponse = httpClient.execute(httpPut);
        } catch (IOException e) {
            K2Assert.connectionException("casHttpClient doPut failed ",e);
        }
        return httpResponse;
    }

    public HttpResponse doDelete(HttpDelete httpDelete) throws Exception {
        HttpResponse httpResponse = null;
        try {
            this.preLogin();
            httpResponse = httpClient.execute(httpDelete);
        } catch (IOException e) {
            K2Assert.connectionException("casHttpClient doDelete failed ",e);
        }
        return httpResponse;
    }

    private void initSslHttpClient() throws Exception {
        if (trustStrategy == null) {
//            trustStrategy = (chain, authType) -> true;
            trustStrategy = new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            };
        }
        if (hostnameVerifier == null) {
//            hostnameVerifier = (hostname, session) -> true;
            hostnameVerifier = new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            };
        }
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(trustStrategy).build();
            LayeredConnectionSocketFactory layeredConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            httpClient = HttpClients.custom().setSSLSocketFactory(layeredConnectionSocketFactory).setDefaultCookieStore(cookieStore).build();
        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
            K2Assert.connectionException("casHttpClient initSslHttpClient failed. ",e);
        }
    }

    private void preLogin() throws Exception {
        if (null == targetUrl) {
            targetUrl = casLoginUrl;
        }
        try {
            String loginUrl = casLoginUrl + "?" + serviceParamName + "=" + new URLCodec(urlEncoding).encode(targetUrl) + "&" + tokenParamName + "=" + token;
            HttpGet loginGet = new HttpGet(loginUrl);
            httpClient.execute(loginGet);
        } catch (EncoderException e) {
            K2Assert.connectionException("URL(String) can't encode to URL(Http). ",e);
        } catch (IOException e) {
            K2Assert.connectionException("preLogin failed. ",e);
        }
    }
}
